//----------------------------------------------------------------------------------------------
//	Filename:	SegmentsGraph.h
//	Author:		Sandro Spina
//	Date:		27/01/2013
//----------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------
#pragma once
#include "UniqueId.h"
#include <map>
#include <stack>
#include <Geometry\Vector3.h>

#include "SegmentsGraph.h"
#include "ObjStructureGraph.h"

namespace Illumina
{
	namespace Core
	{		
		struct GraphCut;

		class StructureGraph
		{
		public:
			::UniqueID * nodeID;

			enum shape {
				line,
				plane,
				sphere,
				complex,
				compound,
				notset
			};

			//More actions can be added here eventually ...
			enum action {
				add_direction,
				transition_count
			};
			
			enum nodetype {
				level0,				//Not part of an internal structure graph, found in compound surface segments.
				level1,				//Part of an internal structure graph, found in a compound surface segment.
				unknown				//Type still not set.
			};

			//Node IDs are the same as corresponding surface segment ids.
			struct EdgeStats
			{
				std::string label;		
				int strength;
				float obb_overlap;
				float dot;
				float epsilon;
				std::vector<action> actions;

				int sizeInBytes()
				{
					int s = 0;
					s += sizeof(int);	//strength
					s += sizeof(float); //obb_overlap
					s += sizeof(float);	//dot
					s += sizeof(float);	//epsilon
					s += sizeof(int) + (sizeof(action) * actions.size());	//count of actions + action ids
					return s;
				}
			};

			struct NodeStats
			{				 
				std::string label;
				int point_count;
				shape shape;
				nodetype type;
				Vector3 orientation;
				Vector3 bounding_min;
				Vector3 bounding_max;
				Vector3 mean_bounding_volume;

				int sizeInBytes()
				{
					int s = 0;
					s += sizeof(int);  //point_count
					s += sizeof(shape);
					s += sizeof(nodetype);
					s += sizeof(float) * 3 * 4;
					return s;
				}
			};

			struct Node
			{
				int id;
				int used;			//Determines whether this node has been used in the solution mappings
				int internalNodeId; //ID of internal node
				NodeStats* stats;
				SegmentsGraph::SurfaceSegment * segment;
				std::map<int, std::pair<EdgeStats*, Node*> *> * edges;

				int sizeInBytes()
				{
					int s = 0;
					s += sizeof(int);	//id
					s += sizeof(int);	//used						
					s += stats->sizeInBytes();
					s += sizeof(int);	//id of segment in segments graph
					s += sizeof(int);   //space to store edge count 
					for (std::map<int, std::pair<EdgeStats*, Node*> *>::iterator it = edges->begin(); it != edges->end(); it++)
					{
						s += sizeof(int); //id of sink node
						s += it->second->first->sizeInBytes();
					}
					return s;
				}

				Node()
					: stats(new NodeStats())
					, used(0)
					, edges(new std::map<int, std::pair<EdgeStats*, Node*>*>())					
				{ }

				~Node()
				{
					delete edges;
					delete stats;
				}
			};	

			SegmentsGraph * m_segmentsGraph;

			std::map<int, int> * m_mergedNodesMap;								//Stores the mapping between the node merged [key] and it's parent merged into node [value]
			std::map<int, std::vector<int>> * m_mergedNodesSeeds;				//Stores the list of nodes which have been merged into. The [key] nodes

			std::map<int, Node*> * m_nodes;
			std::map<int, Node*>::iterator m_nodesIterator;			
	
			Illumina::Core::List<Node*> * m_disjointStructureGraphs;

			std::map<std::string, int> * m_directionsTravelled;	

			StructureGraph(void);

			StructureGraph(SegmentsGraph * _segmentsGraph);

			void Initialise(void);

			//Create and populate a new node to represent a surface segment. Add to nodes map.
			void CreateNewNode(SegmentsGraph::SurfaceSegment * p_ss);

			void AddNodeConnection(SegmentsGraph::SurfaceSegment * p_segment_source, SegmentsGraph::SurfaceSegment * p_segment_sink, int p_strength);

			//Returns a list of indices in breadth first order
			Illumina::Core::List<int> * BreadthFirstTraversal(Node * p_initial);
	
			//Graph Search Functions. map<node id, node>
			std::map<int, std::vector<GraphCut*>*> * SearchSceneGraph(ObjStructureGraph * structure);

			//State Merging functions
			float AutoMergeCoplanar(float _angle, float _epsilon, int _level);
			//Level indicates whether merging of internal states is done or not. If internal than segmentid2 changes and not segment id 1.
			void MergeStates(std::vector<int> _nodes, int _level);
			void MergeSmallStates(int p_min_number_of_points, SegmentsGraph * p_sg); //... small states with adjacent (same type) states, etc.
			void MergeBigStatesAcrossWeakEdges(int p_min_number_of_points, SegmentsGraph * p_sg);
			void MergeBigStatesAcrossWeakEdges(SegmentsGraph * p_sg);

			//Promote intermal Level1 nodes to Level0 nodes. This is done before search/fitting takes place.
			void PromoteInternalNodesToLevel0();
			
			bool IsNodeMerged(int _nodeId);
			std::vector<SegmentsGraph::SurfaceSegment*> NodeMergedWith(int _nodeId);

			//Pruning(i.e. node deletion) Functions
			void PruneSmallStates(int p_min_number_of_points);
			void PruneBigStates(int p_max_number_of_points);
			void PruneWeakEdges(int p_min_number_of_points);

			//Minimisation Algorithms *** 
			//Returns the percentage (between 0 and 1) of reduction on <states,edges> carried out.
			std::pair<float, float> Minimise(float _angle, float _epsilon);

			//Shape Structure Graphs Search
			std::vector<std::vector<int>> SearchCuboids();
			std::vector<std::vector<int>> SearchPlanes();
			std::vector<std::vector<int>> SearchSpheres();
			std::vector<std::vector<int>> SearchCylinders();
			std::vector<std::vector<int>> SearchObject(std::string p_shapeName);
	
			//Graph Connectivity functions for surface segments 
			std::vector<int> AllDirectlyConnectedSegments(int _segmentFromID);
			std::vector<int> AllTransitivelyConnectedSegments(int _segmentFromID);
			std::vector<int> DirectlyConnectedSegmentsAtAngle(int _segmentFromID, float _angle, float _epsilon);
			std::vector<int> TransitivelyConnectedSegmentsAtAngle(int _segmentFromID, float _angle, float _epsilon);
			std::vector<int> DirectlyConnectedCoplanarSegments(int _segmentFromID, float _angle, float _epsilon);
			std::vector<int> TransitivelyConnectedCoplanarSegments(int _segmentFromID, float _angle, float _epsilon);

			//Searches fpr subgraphs (objects) making up the scene
			std::map<int, std::vector<GraphCut*>*> * SearchSceneGraph(std::string &query);

			//Given two nodes, returns pairs of compatible edges.
			bool EdgesCompatible(ObjStructureGraph::Node * p_nodeStructure, StructureGraph::Node * p_nodeScene, std::vector<std::pair<int, int>> * compatible, std::vector<int> * edgeIndices);

			//Check compatibility between p_edge1 of p_node1 and p_edge2 of p_node2
			bool EdgeCompatible(ObjStructureGraph::Node * p_nodeStructure, int p_edge1, StructureGraph::Node * p_nodeScene, int p_edge2);

			std::vector<std::vector<int>> GetNodesSortedBySize();

			//Draw to GraphViz Text Format
			void Draw(std::string p_filepath, int p_parentSegmentId);
			
			//Serialisation Functions
			void WriteToBSTRFile(std::string _filepath);
			bool ReadFromBSTRFile(std::string _filepath); 
		};
	}
}
		
